Skip to content

Add per-container Linux Secret Service for VS Code SecretStorage#22

Draft
Copilot wants to merge 4 commits into
mainfrom
copilot/devcontainer-linux-secret-service
Draft

Add per-container Linux Secret Service for VS Code SecretStorage#22
Copilot wants to merge 4 commits into
mainfrom
copilot/devcontainer-linux-secret-service

Conversation

Copilot AI commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

On Linux, VS Code (and extensions like GitHub Copilot) silently fall back to a plaintext secrets.json for SecretStorage when no Secret Service backend is reachable. This adds a self-contained, headless gnome-keyring setup inside the container so credentials land in an AES-encrypted vault—no host keychain forwarding.

Changes

  • Dockerfile — apt layer adding gnome-keyring, libsecret-1-0, libsecret-tools, dbus, python3-jeepney.
  • keyring-init.sh — idempotent start hook: launches a private DBus session bus on a fixed socket, starts gnome-keyring-daemon --components=secrets, bootstraps the default collection, persists env, and probes a round-trip. Reuses live daemons; degrades gracefully (exit 0) on failure.
  • keyring-bootstrap.py — creates the persistent default collection via gnome-keyring's non-spec CreateWithMasterPassword (the spec CreateCollection path needs a GTK prompter that containers lack). No-ops when the alias already resolves.
  • devcontainer.json — static remoteEnv (XDG_RUNTIME_DIR, DBUS_SESSION_BUS_ADDRESS for UID 1000) so every VS Code Server child finds the same bus, plus postStartCommand/postAttachCommand hooks.
  • README.md — documents the secret-storage capability.

Notes for reviewers

  • The fixed bus path in remoteEnv is load-bearing: without it extensions race the start hook and mark the session as basic-store.
  • Master password defaults to empty; set KEYRING_PASSWORD (e.g. via a Codespaces user secret) to raise the bar. The script feeds it to both bootstrap and --unlock.
  • UID 1000 (node) is hardcoded in the remoteEnv paths; would need substitution on a different base image.

@bmorton bmorton left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot Should we be committing __pycache__ here?

Copilot AI commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

@copilot Should we be committing __pycache__ here?

No—that was committed by accident during validation. Removed it in 42fce39 and added a .gitignore that excludes __pycache__/ and *.py[cod].

Copilot AI requested a review from bmorton June 12, 2026 18:00
@bmorton

bmorton commented Jun 12, 2026

Copy link
Copy Markdown
Owner

@copilot lets add a note to the README about how to use the devcontainer master password without Codespaces.

Copilot AI commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

@copilot lets add a note to the README about how to use the devcontainer master password without Codespaces.

Added a "Setting a master password" section to the README in 3acc730. It covers forwarding KEYRING_PASSWORD from the host shell through remoteEnv via ${localEnv:KEYRING_PASSWORD} for local Dev Containers, alongside the Codespaces user-secret approach, plus how to rotate.

Comment thread README.md
"remoteEnv": {
"XDG_RUNTIME_DIR": "/tmp/runtime-1000",
"DBUS_SESSION_BUS_ADDRESS": "unix:path=/tmp/runtime-1000/bus",
"KEYRING_PASSWORD": "${localEnv:KEYRING_PASSWORD}"

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot are there any risks about keeping this in the environment? is this still more secure than plaintext or no keyring password?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question — yes, it's still strictly better than plaintext or an empty password, with one caveat. I documented the trade-offs in e365430:

  • At rest (the threat this targets): the vault is AES-encrypted with the password, so a snapshot/backup/stolen disk image is useless without it. Empty password = trivially decryptable; plaintext fallback = no protection.
  • At runtime: the password sits in the remote user's environment, so any process running as that user can read it (/proc/<pid>/environ) and unlock the vault. But that same process could already read the secrets directly via SecretStorage, so it doesn't lower the bar below where it already is.
  • Provenance: it's injected at runtime via ${localEnv:...} / Codespaces secret, never baked into the image or committed. The main thing to avoid is hardcoding it in devcontainer.json or leaving it in shell history.

Net: empty password protects almost nothing at rest; a real KEYRING_PASSWORD meaningfully protects the on-disk vault while leaving runtime exposure unchanged.

Copilot AI requested a review from bmorton June 12, 2026 23:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants